package pacman;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;
import sensors.IRSensor;
import sensors.LightSensor;
import sensors.UltrasonicSensor;
import sensors.virtualSensors.VirtualInfraredSensor;
import sensors.virtualSensors.VirtualLightSensor;
import sensors.virtualSensors.VirtualUltrasonicSensor;
import Analyser.BarcodeAnalyser;
import Analyser.DistanceToWall;
import Analyser.SectorAnalyser;
import Analyser.SectorAnalyserV1;
import behaviour.MazeCorrector;
import behaviour.PacmanFinder;
import behaviour.Strategy;

import com.rabbitmq.client.Channel;
import communication.Communicator;
import communication.Config;

public class Ghost extends Robot {
	// De rotatie wordt genomen ten opzichte van de positieve x-as.
	private IRSensor infraredSensor;
	private LightSensor lightSensor;
	private UltrasonicSensor ultrasonicSensor;
	private Strategy currentStrategy;
	public Strategy getCurrentStrategy(){
		return currentStrategy;
	}
	private int sensorRotation; //relative rotation tov de robot, positief is naar links
	public BarcodeAnalyser barcodeAnalyser = new BarcodeAnalyser();
	private boolean pacmanFound;
	public int xOffsetSensor;
	public int yOffsetSensor;
	public Strategy strategy;
	private Strategy secondaryStrategy;
	private Maze[] matchingMazes;
	public int frontCompensation;
	private Coordinate coordinatePacman;
	private boolean hasSendName = false;

	public SectorAnalyser sectorAnalyser = new SectorAnalyserV1();
	private Coordinate[] ghostPositionsArr = {new Coordinate(0,0),new Coordinate(0,0),new Coordinate(0,0),new Coordinate(0,0)};
	/**
	 * The name of this player/robot
	 */
	private String name;

	/**
	 * True if this player thinks he captured pacman
	 */
	private boolean captured;

	/**
	 * The list of sectors this player's robot will drive to
	 */
	private ArrayList<Location> plan;

	/**
	 * Indicates what version of the communication protocol this player uses.
	 */
	private String version;

	/**
	 * The channel this player will use to communicate
	 */
	private Channel channel;
	private Game game;


	public boolean terminated = false;
	public boolean isTravelling = false; //should only be set from within the simulator
	private boolean inBarcodeState;

	/**
	 * Initializes a new Robot located at a given location
	 * 
	 * @param locs
	 * 			The location for this Robot
	 * @param maze
	 * 			The maze where this robot will ride around
	 * @param infraredSensor
	 * 			The infraredSensor this robot will be using\
	 * @param lightSensor
	 * 			The lightSensor this robot will be using
	 * @param ultrasonicSensor
	 * 			The ultrasonicSensor this robot will be using
	 * @throws IOException 
	 */
	public Ghost(Location loc, int rotation, IRSensor infraredSensor, LightSensor lightSensor, UltrasonicSensor ultrasonicSensor, String name, Game game) throws IOException {
		this.setMaze(new Maze(10));
		this.setOtherMazes(new HashMap<String, Maze>());
		this.setLocation(loc);
		this.setRotation(rotation);
		this.sensorRotation=0;
		barcodeAnalyser.whiteMargin = 40;
		barcodeAnalyser.blackMargin = 40;
		this.setInfraredSensor(infraredSensor);
		this.setLightSensor(lightSensor);
		this.setUltrasonicSensor(ultrasonicSensor);
		this.setName(name);
		//nog aanpassen
		this.game = game;
		channel = game.getChannel();
		matchingMazes = new Maze[0];
	}

	/**
	 * Initializes a new Robot with virtual sensors
	 * 
	 * @param loc
	 * 			The location for this Robot
	 * @param maze
	 * 			The maze where this robot will ride around
	 * @throws IOException 
	 */
	public Ghost(Location loc, int rotation, String name, Game game) throws IOException {
		this.setMaze(new Maze(10));
		secondaryStrategy=new MazeCorrector(getMaze());
		this.setOtherMazes(new HashMap<String, Maze>());
		this.setLocation(loc);
		this.setRotation(rotation);
		this.sensorRotation = 0;
		this.addVirtualSensors();
		//this.strategy=new Explorer(loc, this.getMaze());
		this.setName(name);
		this.game = game;
		//channel = game.getChannel();
		matchingMazes = new Maze[0];
		if(game != null)
			channel = game.getChannel();
		//setUpRabbitMQ();
		//getChannel().exchangeDeclare(Config.EXCHANGE_NAME, "fanout");
		// Aankondigen dat de robot het spel wil joinen gebeurt in GhostRunner.
	}
	
	public Ghost(Location loc, int rotation, String name) throws IOException{
		this(loc, rotation, name, null);
	}

	//Zelfde staat in game
//	private void setUpRabbitMQ() throws IOException {
//		ConnectionFactory factory = new ConnectionFactory();
//        factory.setHost(Config.HOST_NAME);
//        Connection connection = factory.newConnection();
//        setChannel(connection.createChannel());
//        
//	}
	
	public void setCoordinatePacman(Coordinate pacman){
		System.out.println("pacmanlocationupdated: "+pacman.getX()+" "+pacman.getY());
		coordinatePacman=pacman;
		pacmanFound=true;
		strategy.setCoordinatePacman(pacman);
		try {
			sendPACMAN(pacman);
		} catch (IOException e) {
			System.out.println("sendpacman fail");
		}
	}
	
	public Direction[] shiftArray(Direction[] directions, int amount){
		for (int j = 0; j < amount; j++) {
			Direction a = directions[directions.length - 1];
            int i;
            for (i = directions.length - 1; i > 0; i--)
            	directions[i] = directions[i - 1];
            directions[i] = a;
		}
		return directions;
	}

	public void run() {

		Timer timer = new Timer();

		BarcodeAnalyserTask barcodeAnalyserTask = new BarcodeAnalyserTask();
		int barcodeUpdatePeriod = 10; //ms (was 10)		

		timer.schedule(barcodeAnalyserTask, 0, barcodeUpdatePeriod);
//		Timer timerIR = new Timer();

//		IRAnalyserTask irAnalyserTask = new IRAnalyserTask(this);
//		int irUpdatePeriod = 10; //ms (was 10)		
//
//		timerIR.schedule(irAnalyserTask, 0, irUpdatePeriod);
		currentStrategy=strategy;


		while (currentStrategy != null) {
			if (!isTravelling)
				updateStrategy();
		}

	}

	private void updateStrategy() {

		Maze maze = this.getMaze();
		Coordinate currentCoordinate = maze.getCoordinateOfLocation(this
				.getLocation());
		if (!maze.hasThisSectorExplored(currentCoordinate)||currentStrategy.exploreEverything()) {
			HashMap<Direction, DistanceToWall> sectorWalls = sectorAnalyser.ExploreSector(this);
			writeWalls(sectorWalls, currentCoordinate);
			correctToWalls(sectorWalls);

			
			Direction[] directions = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
			int walls[] = new int[4];			
			for(int h=0; h<directions.length; h++)
			{
				int wallType = sectorWalls.get(directions[h]).WallType;
				if(wallType == 1)
					walls[h] = 1;
				else
					walls[h] = 0;
			}
			/**
			 * String message = "JOIN\n";
				channel.basicPublish("ORANJE", "", null, message.getBytes());
			 */
			int[] nozw = new int[4];
			nozw = generateInformation(walls, directions);
			String message = "" + getName() + " DISCOVER ";
			resetSensorMotor();
			//[p:coord] [n:trit] [o:trit] [z:trit] [w:trit]\n\n
			message += getMaze().getCoordinateOfLocation(getLocation())
					.toString();
			for (int j = 0; j < 4; j++) {
				message += " " + nozw[j];
			}
			message += "\n";
			if (channel != null)
				try {
					channel.basicPublish(Config.EXCHANGE_NAME, "", null,
							message.getBytes());
				} catch (IOException e) {
					e.printStackTrace();
				}

		}

		if(currentStrategy.nextDirection(getLocation(), this.matchingMazes, this.ghostPositionsArr)==null){
			if(pacmanFound){
				//gewonnen
				System.out.println("we won!");
				currentStrategy=new PacmanFinder(getMaze());
				currentStrategy.setCoordinatePacman(coordinatePacman);
				System.out.println(coordinatePacman.toString());
			}
			else{ // denkt de maze verkend te hebben, maar heeft pacman niet gevonden, dus heeft ergens foute muren gezet en moet opnieuw gaan verkennnen.
				currentStrategy=secondaryStrategy;
				
			}
			
		}
		else
			currentStrategy=strategy;
		//de positions in ghostpositionarray moeten in dezelfde volgorde zitten als de mazes
		for(int i = 0; i<matchingMazes.length; i++) {
			for(Ghost g : this.getGame().getGhosts()) {
				if(matchingMazes[i].equals(mazesOtherRobots.get(g.getName()))) {
					ghostPositionsArr[i] = this.getGame().getGhostPositions().get(g);
				}
			}
		}
		Direction next= currentStrategy.nextDirection(getLocation(), this.matchingMazes, this.ghostPositionsArr);
		System.out.println(matchingMazes.length);
		if(next==null) return;
		Direction current = generateDirection();
		if (next == current.left()) {
			this.rotate(90, false);
		} else if (next == current.opposite()) {
			this.rotate(-180, false);
		} else if (next == current.right()) {
			this.rotate(-90, false);
		}
		registerPacman();
		CorrectionMode mode=CorrectionMode.NONE;
		if (maze.read(currentCoordinate.getX(), currentCoordinate.getY(),next.left(), true) == 1) 
			mode=CorrectionMode.LEFT;
		else if(maze.read(currentCoordinate.getX(),currentCoordinate.getY(), next.right(), true) == 1)
			mode=CorrectionMode.RIGHT;
		
		checkBarcodeState();
		if(!inBarcodeState){
			travelToNextSector(mode, 40);
		}
		else{
			travel(-20, false);
//			setSpeed(200);
			travelToNextSector(CorrectionMode.NONE, 60);
//			setSpeed(360);
			checkBarcodeState();
			inBarcodeState=false;
		}
		
			
//		if (maze.read(currentCoordinate.getX(), currentCoordinate.getY(),next.left(), true) == 1) {
//			if (getCommunicator() != null)
//				travelToNextSector(CorrectionMode.LEFT,40);
//			else
//				this.travel(40, false);
//		} else if (maze.read(currentCoordinate.getX(),
//				currentCoordinate.getY(), next.right(), true) == 1) {
//			if (getCommunicator() != null)
//				travelToNextSector(CorrectionMode.RIGHT,40);
//			else
//				this.travel(40, false);
//		} else {
//			this.travel(40, false);
//		}
	}
	public void setSpeed(int value){
		if(getCommunicator()!=null)
			getCommunicator().setSpeed(value);
	}

	private void checkBarcodeState() {
		Communicator comm = this.getCommunicator();
		if(comm != null)
		{
			inBarcodeState = comm.hasBlackListValues();
		}
	}

	/**
	 * Returns the integer value of this Ghost's direction.
	 * "Hierbij wordt de waarde 
	 * 1 gebruikt voor noord naar zuid,  == ZUID
	 * 2 voor oost naar west, 			 == WEST
	 * 3 voor zuid naar noord, 			 == NOORD
	 * 4 voor west naar oost."			 == OOST
	 */
	public Direction generateDirection() {
		int rot = getRotation() % 360;
		rot = rot < 0 ? (360 - Math.abs(rot)) : rot;

		if (rot > 45 && rot < 135) {
			return Direction.NORTH;
		} else if (rot >= 135 && rot < 225) {
			return Direction.WEST;
		} else if (rot >= 225 && rot < 315) {
			return Direction.SOUTH;
		} else {
			return Direction.EAST;
		}
	}

	/**
	 * Returns the integer value of this Ghost's direction.
	 * "Hierbij wordt de waarde 
	 * 1 gebruikt voor noord naar zuid,  == ZUID
	 * 2 voor oost naar west, 			 == WEST
	 * 3 voor zuid naar noord, 			 == NOORD
	 * 4 voor west naar oost."			 == OOST
	 */
	public int generateDirectionNumber() {
		int rot = getRotation() % 360;
		rot = rot < 0 ? (360 - Math.abs(rot)) : rot;

		if (rot > 45 && rot < 135) {
			return 3;
		} else if (rot >= 135 && rot < 225) {
			return 2;
		} else if (rot >= 225 && rot < 315) {
			return 1;
		} else {
			return 4;
		}
	}

	/**
	 * Writes walls in the local maze. Given Map must contain an entry for every direction!
	 */
	private void writeWalls(HashMap<Direction, DistanceToWall> sectorWalls,
			Coordinate co) {
		Maze maze = this.getMaze();
		maze.write(co.getX(), co.getY(), Direction.NORTH,
				(char) sectorWalls.get(Direction.NORTH).WallType, true);
		maze.write(co.getX(), co.getY(), Direction.EAST,
				(char) sectorWalls.get(Direction.EAST).WallType, true);
		maze.write(co.getX(), co.getY(), Direction.SOUTH,
				(char) sectorWalls.get(Direction.SOUTH).WallType, true);
		maze.write(co.getX(), co.getY(), Direction.WEST,
				(char) sectorWalls.get(Direction.WEST).WallType, true);
	}

	private void correctToWalls(HashMap<Direction, DistanceToWall> walls) {		
		if(this.getCommunicator() == null)
			return;
		
		Direction robotDirection = generateDirection();
		
		boolean checkedHorizontal = false;
		boolean checkedVertical = false;
		for(Direction dir:walls.keySet())
		{			
			if((dir == Direction.EAST || dir == Direction.WEST) && walls.get(dir).WallType == 1 && !checkedHorizontal)
			{
				getInMiddle(robotDirection, dir, walls.get(dir).Distance);
				checkedHorizontal = true;
			}
			if((dir == Direction.NORTH || dir == Direction.SOUTH) && walls.get(dir).WallType == 1 && !checkedVertical)
			{
				getInMiddle(robotDirection, dir, walls.get(dir).Distance);
				checkedVertical = true;
			}
		}		
	}
	
	private void getInMiddle(Direction ownDir, Direction wallDir, int distance)
	{
		int rotationToCorrect = ownDir.PositiveAngleToDirection(wallDir);
		if(wallDir==ownDir)
			distance-=frontCompensation;
		if (distance < 16) {	
					//(this.getSensorRotation() % 360 + 360) % 360;
			if (rotationToCorrect > 180)
				rotationToCorrect = rotationToCorrect - 360;
			this.rotate(rotationToCorrect, true);
			this.travel(distance - 16, true);
			this.rotate(-rotationToCorrect, true);
		} else if (distance > 19) {
			//int rotationToCorrect = (this.getSensorRotation() % 360 + 360) % 360;
			if (rotationToCorrect > 180)
				rotationToCorrect = rotationToCorrect - 360;
			else if (rotationToCorrect == 180)
				rotationToCorrect = 0;
			this.rotate(-rotationToCorrect, true);
			this.travel(-distance + 19, true);
			this.rotate(rotationToCorrect, true);
		}
	}

	public void travelToNextSector(CorrectionMode mode, int distance) {
		int sign = 0;
		if(mode==CorrectionMode.LEFT)
			sign=1;
		else if (mode == CorrectionMode.RIGHT)
			sign=-1;
		rotateSensorMotor(sign * 90);
		if(getCoordinatePacman()!=null&&getCoordinatePacman().equals(Coordinate.getCoordinateInDirection(getMaze().getCoordinateOfLocation(this.getLocation()), this.generateDirection()))){
			resetSensorMotor();
			return;
		}
		int begin = getUltrasonicValue();
		Direction robotDirection = generateDirection();
		if(begin<=25){
			if(mode==CorrectionMode.LEFT)
				getInMiddle(robotDirection, robotDirection.left(), begin);
			else if(mode==CorrectionMode.RIGHT)
				getInMiddle(robotDirection, robotDirection.right(), begin);
		}
		begin = getUltrasonicValue();
		travel(distance, false);
		int end = this.getUltrasonicValue();
		resetSensorMotor();
		if (end > 25||begin>25)
			return;
		if (Math.abs(begin - end) > 2) {
			int angle = Math.round(getAngle(begin, end));
			this.rotate(sign*angle, true);
		}
		robotDirection = generateDirection();
		if(mode==CorrectionMode.LEFT)
			getInMiddle(robotDirection, robotDirection.left(), end);
		else if(mode==CorrectionMode.RIGHT)
			getInMiddle(robotDirection, robotDirection.right(), end);
		registerPacman();
		
	}
	public Coordinate getCoordinatePacman() {
		// TODO Auto-generated method stub
		return coordinatePacman;
	}

	private void registerPacman() {
		int directionIR;
		if(getCommunicator()!=null){
			directionIR = getCommunicator().getDirectionIR();
			if(directionIR==5||directionIR==4||directionIR==6){
				Coordinate pacman=Coordinate.getCoordinateInDirection(getMaze().getCoordinateOfLocation(getLocation()), generateDirection());
				if(pacman!=null)
					setCoordinatePacman(pacman);
			}
		}
		else{
			directionIR=this.infraredSensor.getDirection();
			if(directionIR==5){
				Coordinate pacman=Coordinate.getCoordinateInDirection(getMaze().getCoordinateOfLocation(getLocation()), generateDirection());
				if(pacman!=null)
					setCoordinatePacman(pacman);
			}
		}
		
	}
	
	private int interpretPositionSensor(int sensorRotation, int xOffsetSensor, int yOffsetSensor, int distance) {
		int result= distance;
		int sensorRot = (sensorRotation%360+360)%360;
		switch(sensorRot){
		case 0: result+=xOffsetSensor+frontCompensation;
		break;
		case 90: result+=yOffsetSensor;
		break;
		case 180: result-=xOffsetSensor;
		break;
		case 270: result-=yOffsetSensor;
		break;
		}
		return result;
	}
	public int getUltrasonicValue(){
		return interpretPositionSensor(sensorRotation, xOffsetSensor, yOffsetSensor, this.getUltrasonicSensor().getDistance());
	}

 	private float getAngle(int begin, int end) {
		double v = Math.abs(begin - end);
		float angle = (float) Math.atan(v / 40); //Nog in radians	
		angle *= 180 / Math.PI;
		if (begin > end) {
			angle *= -1;
		}
		return angle; //In graden
	}

	//	private Direction[] exploreSector(Maze maze, Coordinate currentCoordinate, Direction[] directions) throws IOException {
	//		int i=0;
	//		int [] walls = new int[4];
	//		while(i<4){
	//			/**
	//			 * Een waarde van 0 duidt aan dat er geen obstakel
	//			aanwezig is. De waarde 1 duidt op een obstakel . Ten slotte
	//			duidt de waarde 2 er op dat de noordzijde niet is onderzocht.
	//			 */
	//			int distance=ultrasonicSensor.getDistance();
//				distance = interpretPositionSensor(distance);
	//			if(distance<25){
	//				maze.write(currentCoordinate.getX(),currentCoordinate.getY() ,directions[i], (char) 1, true);
	//				walls[i] = 1;
	//				
	//				if(distance<15&&getCommunicator()!=null){
	//					int rotationToCorrect=(sensorRotation%360+360)%360;
	//					if(rotationToCorrect>180)
	//						rotationToCorrect=rotationToCorrect-360;
	//					this.rotate(rotationToCorrect, false);
	//					this.travel(distance-14, true);
	//					this.rotate(-rotationToCorrect, false);
	//				}
	//				else if(distance>19&&getCommunicator()!=null){
	//					int rotationToCorrect=(sensorRotation%360+360)%360;
	//					if(rotationToCorrect>180)
	//						rotationToCorrect=rotationToCorrect-360;
	//					else if (rotationToCorrect==180)
	//						rotationToCorrect=0;
	//					this.rotate(-rotationToCorrect, false);
	//					this.travel(-distance+18, true);
	//					this.rotate(rotationToCorrect, false);
	//				}
	//			}
	//			//nog geen rekening gehouden dat 255 errorwaarde kan zijn
	//			else{
	//				maze.write(currentCoordinate.getX(),currentCoordinate.getY() ,directions[i], (char) 2, true);
	//				walls[i] = 0;
	//			}
	//			rotateSensorMotor(-90);
	//			i++;
	//		}
	//		/**
	//		 * String message = "JOIN\n";
	//			channel.basicPublish("ORANJE", "", null, message.getBytes());
	//		 */
	//		int [] nozw = new int [4];
	//		nozw = generateInformation(walls, directions);
	//		String message = "" + getName() + " DISCOVER ";
	//		resetSensorMotor();
	//		//[p:coord] [n:trit] [o:trit] [z:trit] [w:trit]\n\n
	//		message += getMaze().getCoordinateOfLocation(getLocation()).toString();
	//		for(int j = 0; j < 4; j++){
	//			message += " " + nozw[j];
	//		}
	//		message+= "\n";
	//		if(channel != null)
	//			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());
	//		return directions;
	//	}

	public void resetSensorMotor() {
		if (getCommunicator() != null)
			getCommunicator().resetSensorMotor();
		sensorRotation = 0;
	}

	private int[] generateInformation(int[] walls, Direction[] directions) {
		int[] nozw = new int[4];
		int indexNorth = -1;
		for (int i = 0; i < walls.length; i++) {
			if (directions[i] == Direction.NORTH) {
				indexNorth = i;
			}
		}
		switch (indexNorth) {
		case (0):
			return walls;
		case (1):
			return shiftArray(walls, 3);
		case (2):
			return shiftArray(walls, 2);
		case (3):
			return shiftArray(walls, 1);
		}
		return nozw;
	}

	private int[] shiftArray(int[] walls, int amount) {
		for (int j = 0; j < amount; j++) {
			int a = walls[walls.length - 1];
			int i;
			for (i = walls.length - 1; i > 0; i--)
				walls[i] = walls[i - 1];
			walls[i] = a;
		}
		return walls;
	}

	public void rotateSensorMotor(int degrees) {
		Communicator comm = this.getCommunicator();
		if (comm != null) {
			comm.rotateSensorMotor(degrees);
			//sleep(70);
		}

		sensorRotation += degrees;
		//		sensorRotation = sensorRotation%360;

	}

	private void sleep(int milli) {
		try {
			Thread.sleep(milli);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public int getSensorRotation() {
		return this.sensorRotation;
	}

	/**
	 * Call this method if and only if this ghost is a real one.
	 */
	public void SetupCommunicator() {
		if (getCommunicator() == null)
			setCommunicator(new Communicator());
	}

	/**
	 * Add all the virtual sensors to the robot
	 */
	public void addVirtualSensors() {
		VirtualLightSensor lightSensor = new VirtualLightSensor(this);
		this.setLightSensor(lightSensor);
		VirtualInfraredSensor infraredSensor = new VirtualInfraredSensor(this);
		this.setInfraredSensor(infraredSensor);
		VirtualUltrasonicSensor ultrasonicSensor = new VirtualUltrasonicSensor(
				this);
		this.setUltrasonicSensor(ultrasonicSensor);
	}

	public IRSensor getInfraredSensor() {
		return infraredSensor;
	}

	public void setInfraredSensor(IRSensor infraredSensor) {
		this.infraredSensor = infraredSensor;
	}

	public LightSensor getLightSensor() {
		return lightSensor;
	}

	public void setLightSensor(LightSensor lightSensor) {
		this.lightSensor = lightSensor;
	}

	public UltrasonicSensor getUltrasonicSensor() {
		return ultrasonicSensor;
	}

	public void setUltrasonicSensor(UltrasonicSensor ultrasonicSensor) {
		this.ultrasonicSensor = ultrasonicSensor;
	}

	/**
	 * Invokes the Update() method of the BarcodeAnalyser every period when scheduled.
	 * Takes appropriate action when a barcode is detected.
	 */
	class BarcodeAnalyserTask extends TimerTask {
		@Override
		public void run() {
			if (!terminated) {
				if (!isTravelling)
					return;
				String bc = "";
				if (getLightSensor() instanceof VirtualLightSensor) {
					bc = barcodeAnalyser.Update(lightSensor.readValue());
				}
				else
					bc= getCommunicator().getBarcode();
				if (!bc.equals("")) {
					System.out.println("Barcode detected: " + bc);
				}
				try {
					int barcodeCode = generateBarcodeCode(bc);
					if (barcodeCode != 0) {
						Coordinate coord = getMaze().getCoordinateOfLocation(getLocation());
						sendBARCODEAT(coord, barcodeCode, generateDirectionNumber());
						//if dirction is horizontal -> 1 else 0
						int direction = (generateDirectionNumber() == 4 || generateDirectionNumber() == 2) ? 1
								: 0;
						System.out.println("add barcode:" + bc);
						getMaze().addBarcode(coord.getX(), coord.getY(), bc,
								direction);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			} else {
				this.cancel();
			}
		}
	}
	class IRAnalyserTask extends TimerTask{
		private Ghost ghost;
		public IRAnalyserTask(Ghost ghost){
			this.ghost=ghost;
		}
		
		public void run(){
			if(!terminated){
				if(getCommunicator().getDirectionIR()==5||getCommunicator().getDirectionIR()==4||getCommunicator().getDirectionIR()==6){
					Coordinate pacman=Coordinate.getCoordinateInDirection(ghost.getMaze().getCoordinateOfLocation(ghost.getLocation()), ghost.generateDirection());
					if(pacman!=null)
						ghost.setCoordinatePacman(pacman);
				}
			}
		}
	}

	/**
	 * Returns the code representation of a barcode.
	 * "Een binaire barcode van de vorm abcdef (waarbij elke
		letter een 1 of een 0 voorstelt)1 wordt voorgesteld door het gehele getal
		25 · a + 24 · b + 23 · c + 22 · d + 21 · e + 20 · f ."
	 * @param barcode
	 * @return
	 */
	public int generateBarcodeCode(String barcode) {
		if (barcode.equals(""))
			return 0;
		int code = 0;
		int macht = 0;
		for (int i = (barcode.length() - 1); i >= 0; i--) {
			int waarde = Character.getNumericValue(barcode.charAt(i));
			code += waarde * Math.pow(2, macht);
			macht++;
		}
		return code;
	}

	/**
	 * Processes the POSITION command
	 * @deprecated VERVANGEN DOOR PROCESSPOSITION IN GAME
	 * Het is logischer dat game de posities van alle robots bijhoudt. 
	 * Als elke ghost de posities van de 3 andere ghosts bijhoudt zit je met onnodige duplicatie
	 */
	private void processPOSITION(String name, Coordinate coord) {
		//NOP;
	}

	/**
	 * Processes the DISCOVER command
	 */
	public void processDISCOVER(Coordinate coord, int north, int east,	int south, int west, boolean ownCoord,String name) {
		if(!name.equals(getName())){
			this.getAllMazes().get(name).write(coord.getX(), coord.getY(), maze.Direction.NORTH, (char) north, ownCoord);
			this.getAllMazes().get(name).write(coord.getX(), coord.getY(), maze.Direction.EAST, (char) east, ownCoord);
			this.getAllMazes().get(name).write(coord.getX(), coord.getY(), maze.Direction.SOUTH, (char) south, ownCoord);
			this.getAllMazes().get(name).write(coord.getX(), coord.getY(), maze.Direction.WEST, (char) west, ownCoord);
		}
	}

	/**
	 * Processes the CAPTURED command
	 *
	 */
	public void processCAPTURED() {
		this.setCaptured(true);
	}

	/**
	 * Processes the PLAN command.
	 */
	public void processPLAN(ArrayList<Location> plan) {
		this.setPlan(plan);
	}

	/**
	 * Processes the CANCELPLAN command.
	 */
	public void processCANCELPLAN() {
		this.setPlan(null);

	}

	/**
	 * Processes the PING command
	 * @throws IOException 
	 */
	public void processPING(String sender, String message) throws IOException{
		sendPONG(sender, message);
	}

	/**
	 * Processes the PONG command
	 */
	public void processPONG(String message) {
		//Ja wat doe je hier mee? 
	}

	public void sendJOIN() throws IOException {
		String message = "JOIN\n";
		if (channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null,
					message.getBytes());
	}

	public void sendNAME() throws IOException {
		if(!hasSendName){
			String message = getName() + " NAME " + "1.0\n";
			if (channel != null)
				channel.basicPublish(Config.EXCHANGE_NAME, "", null,
						message.getBytes());
		}
		setHasSendName(true);
	}

	public void sendPOSITION() throws IOException {
		String message = getName() + " POSITION "
				+ getMaze().getCoordinateOfLocation(getLocation()).toString()
				+ "\n";
		if (channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null,
					message.getBytes());
	}

	public void sendDISCOVER(int n, int o, int z, int w) throws IOException {
		String message = getName() + " DISCOVER " + getMaze().getCoordinateOfLocation(getLocation()).toString() + " " + n + " " + o + " " + z + " " + w + "\n";
		if (channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());
	}
	
	public void sendBARCODEAT(Coordinate ownCoordinate, int code, int direction) throws IOException {
		if (code == 0)
			return;
		String message = getName() + " BARCODEAT " + ownCoordinate.getX() + "," + ownCoordinate.getY() + " " + code + " " + direction + "\n";
		if(channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());	

 	}
	public void sendPACMAN(Coordinate coord) throws IOException {
		String message = getName() + " PACMAN " + coord.toString() + "\n";
		if (channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());
	}
	
	public void sendPLAN(ArrayList<Location> plan) throws IOException{
		String message = getName() + " PLAN";
		for(Location loc: plan){
			message += " " + loc.toString();
		}
		message += "\n";
		if(channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());	
	}
	
	public void sendCANCELPLAN() throws IOException{
		String message = getName() + " CANCELPLAN\n";
		if(channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());	
	}
	
	public void sendPING(String destination, String string) throws IOException {
		String message = getName() + " PING " + destination + " " + string + "\n";
		if (channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());
	}
	
	public void sendPONG(String destination, String string) throws IOException {
		String message = getName() + " PONG " + destination + " " + string + "\n";
		if (channel != null)
			channel.basicPublish(Config.EXCHANGE_NAME, "", null, message.getBytes());
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public boolean hasCaptured() {
		return captured;
	}

	private void setCaptured(boolean captured) {
		this.captured = captured;
	}
	
	public ArrayList<Location> getPlan() {
		return plan;
	}

	private void setPlan(ArrayList<Location> plan) {
		this.plan = plan;
	}
	
	public String getVersion() {
		return version;
	}

	void setVersion(String version) {
		this.version = version;
	}
	
	public Channel getChannel() {
		return channel;
	}

	private void setChannel(Channel channel) {
		this.channel = channel;
	}
	public void addOtherRobotMaze(String name){
		this.setOtherRobot(name);
	}
	public void addOtherRobotsMaze(ArrayList<String> names){
		for(int i=0;i<names.size();i++){
			String name = names.get(i);
			this.setOtherRobot(name);
		}
	}
	public void addMatchingMaze(Maze maze){
		if(checkMazes(maze)){
			int size = matchingMazes.length;
			Maze[] tempMaze = new Maze[size];
			for(int i=0; i<matchingMazes.length;i++){
				tempMaze[i] = matchingMazes[i];
			}
			matchingMazes = new Maze[size+1];
			for(int i=0; i<tempMaze.length;i++){
				matchingMazes[i] = tempMaze[i];
			}
			matchingMazes[size] = maze;
		}
	}
	public boolean checkMazes(Maze maze){
		if(maze == null)
			return false;
		else{
			for(int i=0; i<matchingMazes.length;i++){
				if(matchingMazes[i] == maze)
					return false;
			}
		}
		return true;
	}
	public Game getGame() {
		return game;
	}

	private void setGame(Game game) {
		this.game = game;
	}

	public boolean hasSendName() {
		return hasSendName;
	}

	public void setHasSendName(boolean hasSendName) {
		this.hasSendName = hasSendName;
	}

	public String toString() {
		return getName() + " " + getLocation() + " " + getRotation();
	}
	public void setStrategy(Strategy strategy){
		this.currentStrategy = strategy;
		this.strategy=strategy;
	}

	public void lastPreparations() {
		ArrayList<String> names = new ArrayList<String>();
		System.out.println("Grootte getGhosts:" + getGame().getGhosts().length);
		for(Ghost g: getGame().getGhosts()){
			if(!g.getName().equals(this.getName()))	names.add(g.getName());
		}
		addOtherRobotsMaze(names);
		HashMap<String, Maze> test = getAllMazes();
		Set<String> keys = test.keySet();
		System.out.println("LastPREP van robot " + getName());
		for(String key: keys){
			System.out.println(key.toString() + "\n" + test.get(key).toString());
		}
	}
}

